﻿///<reference path="../typings/tsd.d.ts" />
/*使用说明
该功能需要配合SocketData来使用。目前主要功能是ajax请求和广播功能。
1、建立连接  $.auto.setupWS(ip,port)   这会生成$.auto.ws变量
2、代替jax请求  $.auto.ajax(url,{data:d}).done(function(){});
3、广播功能:$.auto.join("room",function(){房间内有消息执行});
            $.auto.leave("room");
            $.auto.broadcast("room",data);  广播数据可能 是字符串或对象
*/
module AUTO {
    export class AUTOWEBSOCKET {
        public ws: WebSocket;  //一个页面共享一个连接
        private ident_id: number = 0;  //每次请求，都增加一次，表示页面请求计数。返回时，也靠它来找到方法。
        private defferd_map: Object = {}; //消息的延迟map，一个deffer只能用一次
        private cb_map: Object = {};//回调map，绑定函数，可以执行多次
        constructor(ip: string, port: number) {
            let scope = this;
            try {
                this.ws = new WebSocket(`ws://${ip}:${port}`);
                this.ws.onmessage = this.onmessage.bind(this);
                this.ws.onerror = this.onerror.bind(this);
                this.ws.onclose = this.onclose.bind(this);
            } catch (e) {
                this.ws = null;
            }
        }
        private onmessage(msg: MessageEvent) {
            //返回数据增加RESULT字段，是实际响应数据
            var data = JSON.parse(msg.data);
            if (data._ident_id) {
                //如果是ajax的消息
                var id = data._ident_id;
                var def = <JQueryDeferred<{}>>this.defferd_map[id];
                if (data._exception) def.reject(data._exception);
                else def.resolve(data._data);
            } else if (data._room) {
                //如果是房间（广播）消息
                var room = data._room;
                this.cb_map[room] && this.cb_map[room](data._data);
            }
        }
        private onerror(ev: Event) {
            console.log("错误提示", "系统连接WebSocket服务时，发生异常！");
        }
        private onclose(ev: CloseEvent) {
            console.log("提示", "系统已经断开WebSocket服务连接！");
        }
        private support(): boolean {
            if (!this.ws) console.log("浏览器不支持WebSocket!");
            return false;
        }
        //防止正在连接中，需要延迟发送
        private send(content: string) {
            let scope = this;
            if (this.ws.readyState == this.ws.CONNECTING) {
                setTimeout(function () { scope.send(content); }, 500);
            } else if (this.ws.readyState == this.ws.OPEN) {
                this.ws.send(content);
            }
        }
        //请求及返回时，都要带着_requestpath  _ident_id的参数
        //返回一个def，
        //setting的属性只使用data一个，这里使用JQueryAjaxSettings，目的是替代时，不报ts错误。
        public ajax(url: string, setting: JQueryAjaxSettings): JQueryPromise<any> {
            if (!this.support()) return $.Deferred();
            //url 必须是/api/control/action 格式  替换，然后把第一个空值去掉
            url = url.replace(/\\/g, '/')
            var para = url.split("/");
            para[0] || para.shift();

            //为id生成一个$.defer,保存起来
            var def = $.Deferred();
            this.defferd_map[++this.ident_id] = def;
            //发送请求
            var data = {
                _requestpath: `${para[1]}/${para[2]}`,
                _data: setting.data,
                _ident_id: this.ident_id
            }
            this.send("api" + " " + JSON.stringify(data));
            return def.promise();
        }
        //加入一个房间，相当于订阅一下。
        //暂时不增加事件链了。这样一个房间只有一个消息
        public join(room: string, onRoomMsg: Function) {
            if (!this.support()) return;
            this.cb_map[room] = onRoomMsg;
            var msg = { _room: room, _leave: 0 };
            this.send("join " + JSON.stringify(msg));
        }
        //离开一个房间
        public leave(room: string) {
            if (!this.support()) return;
            var msg = { _room: room, _leave: 1 };
            this.send("join " + JSON.stringify(msg));
            delete this.cb_map[room];
        }
        //向房间内发布一个信息
        public broadcast(room: string, data: string | Object) {
            if (!this.support()) return;
            var msg = { _room: room, _data: data };
            this.send("broadcast " + JSON.stringify(msg));
        }
    }
}